/*
  1. Promise 就是一个类 在执行这个类的时候 需要传递一个执行器进去 执行器会立即执行
  2. Promise 中有三种状态 分别为 成功 fulfilled 失败 rejected 等待 pending
    pending -> fulfilled
    pending -> rejected
    一旦状态确定就不可更改
  3. resolve和reject函数是用来更改状态的
    resolve: fulfilled
    reject: rejected
  4. then方法内部做的事情就判断状态 如果状态是成功 调用成功的回调函数 如果状态是失败 调用失败回调函数 then方法是被定义在原型对象中的
  5. then成功回调有一个参数 表示成功之后的值 then失败回调有一个参数 表示失败后的原因
  6. 同一个promise对象下面的then方法是可以被调用多次的
  7. then方法是可以被链式调用的, 后面then方法的回调函数拿到值的是上一个then方法的回调函数的返回值
*/


const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

class MyPromise {
  staus = PENDING
  value = undefined
  reason = undefined
  successFn = []
  failedFn = []

  constructor (executor) {
    try {
      executor(this.resolve, this.reject)
    } catch (error) {
      this.reject(error)
    }
  }

  resolve = value => {
    if (this.staus !== PENDING) return
    this.staus = FULFILLED
    this.value = value
    while(this.successFn.length) this.successFn.shift()() // 多次调用，添加处理函数
  }
  
  reject = reason => {
    if (this.staus !== PENDING) return
    this.staus = REJECTED
    this.reason = reason
    while(this.failedFn.length) this.failedFn.shift()() // 多次调用，添加处理函数
  }

  then = (successFn, failedFn) => {
    // 将then方法的参数变为可以传递下去
    successFn = successFn ? successFn : value => value
    failedFn = failedFn ? failedFn : reason => { throw reason }
    // 为实现then的链式调用
    let promise2 = new MyPromise((resolve, reject) => {
      if (this.staus === FULFILLED) {
        // 避免 promise自己循环调用
        try {
          setTimeout(() => {
            // 如果当前是返回的普通值，直接resolve
            // 如果是 promise 对象，查看其结果。决定调用 resolve, reject
            const x = successFn(this.value)
            resolvePromise(promise2, x, resolve, reject)
          }, 0)
        } catch (error) {
          reject(error)
        }
      } else if (this.staus === REJECTED) {
        try {
          setTimeout(() => {
            const x = failedFn(this.reason)
            resolvePromise(promise2, x, resolve, reject)
          }, 0)
        } catch (error) {
          reject(error)
        }
      } else { 
        // 等待状态为处理异步状态
        // 讲成功回调，失败回调存储起来
        this.successFn.push(() => {
          try {
            setTimeout(() => {
              // 如果当前是返回的普通值，直接resolve
              // 如果是 promise 对象，查看其结果。决定调用 resolve, reject
              const x = successFn(this.value)
              resolvePromise(promise2, x, resolve, reject)
            }, 0)
          } catch (error) {
            reject(error)
          }
        })
        this.failedFn.push(() => {
          try {
            setTimeout(() => {
              const x = failedFn(this.reason)
              resolvePromise(promise2, x, resolve, reject)
            }, 0)
          } catch (error) {
            reject(error)
          }
        })
      }
    })
    return promise2
  }

  finally (callback) {
    return this.then(
      value => MyPromise.resolve(callback()).then(() => value),
      reason => MyPromise.resolve(callback()).then(() => { throw reason })
    )
  }

  catch (failCallBack) {
    return this.then(undefined, failCallBack)
  }

  static all (array) {
    let result = []
    let count = 0
    return new MyPromise((resolve, reject) => {
      function addFn(value, index) {
        result[index] = value
        count++
        if (count === array.length) resolve(result)
      }
      array.forEach((ele, index) => {
        if (ele instanceof MyPromise) {
          ele.then(value => addFn(value, index), error => reject(error))
        } else {
          addFn(ele, index)
        }
      })
    })
  }

  static resolve (value) {
    if (value instanceof Promise) return value
    return new MyPromise(resolve => resolve(value))
  }
}

function resolvePromise(promise, x, resolve, reject) {
  if (promise === x) return reject(new TypeError('promise 不能自我循环调用'))
  if (x instanceof MyPromise) {
    x.then(resolve, reject)
  } else {
    resolve(x)
  }
}

module.exports = MyPromise
